/** @file csv_parser.h
 *
 *  @author Dongryeol Lee (drselee@gmail.com)
 */

#ifndef CORE_DATA_CSV_PARSER_H
#define CORE_DATA_CSV_PARSER_H

#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include "core/data/matrix_dataset.h"
#include "core/data/parser_util.h"

namespace core {
namespace data {

class CSVParser {
  private:
    friend class core::data::MatrixDataset;

  private:

    static bool Parse(
      const std::string &file_name_in, core::data::MatrixDataset *dataset_out,
      int begin_char_in = -1, int count_in = std::numeric_limits<int>::max()) {

      // Open the file.
      std::ifstream stream(file_name_in.c_str());
      if(! stream.is_open()) {
        std::cerr << "Failed to open " << file_name_in << "...\n";
        return false ;
      }

      // The first row is the list of attribute list.
      core::data::ParserUtil::ParseLine(stream, &dataset_out->attribute_names_);
      std::cerr << "Detecting the following attribute names...\n";
      std::cerr << "A total of " << dataset_out->attribute_names_.size() <<
                " attributes...\n";
      for(int i = 0;
          i < static_cast<int>(dataset_out->attribute_names_.size()); i++) {
        std::cerr << dataset_out->attribute_names_[i];
        if(i < static_cast<int>(dataset_out->attribute_names_.size()) - 1) {
          std::cerr << ",";
        }
      }
      std::cerr << "\n";

      // Count the number of lines.
      int failed_line_num;
      int num_attributes = dataset_out->attribute_names_.size();
      int num_points =
        core::data::ParserUtil::CountNumLines(
          stream, num_attributes, begin_char_in, count_in, true, &failed_line_num, NULL);
      if(num_points < 0) {
        std::cerr << "Line " << failed_line_num <<
                  " contains a different number of tokens...\n";
        return false;
      }

      // Now parse the actual.
      dataset_out->Init(num_attributes, num_points);
      std::cerr << "Reading in " << num_points << " points...\n";
      std::vector<std::string> parsed_line;
      for(int i = 0; i < num_points; i++) {
        arma::vec &column = dataset_out->get_instance(i);

        // For the moment, assume that all values are numeric.
        core::data::ParserUtil::ParseLine(stream, &parsed_line);
        for(int j = 0; j < num_attributes; j++) {
          column[j] = atof(parsed_line[j].c_str());
        }
      }
      return true;
    }
};
}
}

#endif
